package assist.client.gwt.view;


import java.util.Map;
import java.util.TreeMap;

import assist.client.gwt.service.GamesProvider;
import assist.client.gwt.service.PlayersProvider;
import assist.client.gwt.service.StatsProvider;
import assist.client.gwt.view.activity.AppContainerPresenter;
import assist.client.gwt.view.ui.AppContainer;
import assist.client.gwt.view.ui.AppContainerImpl;
import assist.client.gwt.view.ui.SelectGamesView;
import assist.client.gwt.view.ui.SelectGamesViewImpl;
import assist.client.gwt.view.ui.SelectStatsView;
import assist.client.gwt.view.ui.SelectStatsViewImpl;
import assist.client.gwt.view.ui.ShowGamesView;
import assist.client.gwt.view.ui.ShowGamesViewImpl;
import assist.client.gwt.view.ui.ShowStatsView;
import assist.client.gwt.view.ui.ShowStatsViewImpl;
import assist.domain.model.Team;

import com.google.gwt.event.shared.SimpleEventBus;
import com.google.gwt.place.shared.PlaceController;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.user.client.ui.Widget;
import com.google.web.bindery.event.shared.EventBus;

/**
 * Abstract Factory provides default implementation of the views.
 * Subclasses MUST provide the UI Binder for the layouts.
 * Subclasses can OPTIONALLY provide different View implementations as well,
 * if there is different functionality needed.
 * So only the UI binder layout is needed if you want to use different styles,
 * widget types, etc.
 * But, if you need different functionality, like responding to a different
 * button, then also need new Impl.  
 *
 */
public abstract class AbstractClientFactory implements ClientFactory
{   
    private static final EventBus eventBus = new SimpleEventBus();
    private static final GamesProvider gamesProvider = new GamesProvider(eventBus);
    private static final PlaceController placeController = new PlaceController(eventBus);        
    private static SelectGamesView selectGamesView = null;        
    private static SelectStatsView selectStatsView = null;  
    private static ShowStatsView showStatsView = null; 
    private static AppContainer appContainer;
    private static ShowGamesView showGamesView;
    private static Map<Team, PlayersProvider> playersProviders = new TreeMap<Team, PlayersProvider>();
    private static Map<Team, StatsProvider> statsProviders = new TreeMap<Team, StatsProvider>();
    
    protected AbstractClientFactory()
    {        
        if (selectGamesView == null)
        {
            selectGamesView = new SelectGamesViewImpl(getSelectGamesViewBinder());    
        }
        if (selectStatsView == null)
        {        
            selectStatsView = new SelectStatsViewImpl(getSelectStatsBinder());
        }
        if (showStatsView == null)
        {        
            showStatsView = new ShowStatsViewImpl(getShowStatsViewBinder());
        }
        if (showGamesView == null)
        {        
            showGamesView = new ShowGamesViewImpl(gamesProvider, 
                    getShowGamesViewBinder());
        }   
        if (appContainer == null)
        {        
            appContainer = new AppContainerImpl(
                    (ShowGamesViewImpl)showGamesView, getAppContainerBinder());
        }
        //NOTE: IF/WHEN the AppContainer view itself needs to communicate with
        //Event Bus/Place Controller, then it will also need this presenter.
        //For now, it is just the views within the container that are not
        //part of the Main Activity/View framework that need this presenter.
        AppContainerPresenter appContainerPresenter = new AppContainerPresenter(this);
        showGamesView.setPresenter(appContainerPresenter);
    }
        
    protected abstract UiBinder<Widget, SelectGamesViewImpl> getSelectGamesViewBinder();
    protected abstract UiBinder<Widget, SelectStatsViewImpl> getSelectStatsBinder();
    protected abstract UiBinder<Widget, ShowStatsViewImpl> getShowStatsViewBinder();
    protected abstract UiBinder<Widget, ShowGamesViewImpl> getShowGamesViewBinder();
    protected abstract UiBinder<Widget, AppContainerImpl> getAppContainerBinder();

    @Override
    public EventBus getEventBus()
    {
        return eventBus;
    }
    @Override
    public PlaceController getPlaceController()
    {
        return placeController;
    }    
    
    @Override
    public GamesProvider getGamesProvider()
    {
        return gamesProvider;
    }     

    @Override
    public StatsProvider getStatsProvider()
    {
        Team team = gamesProvider.getGamesQuery().getTeam();
        if (AbstractClientFactory.statsProviders.get(team) == null)
        {
            AbstractClientFactory.statsProviders.put(team, 
                    new StatsProvider(eventBus, 
                            gamesProvider, getDefaultPlayersProvider()));
        }
        return AbstractClientFactory.statsProviders.get(team);
    }    
    
    /**
     * Gets the PlayersProvider for the currently selected team in the games
     * @param team
     * @return
     */
    @Override
    public PlayersProvider getDefaultPlayersProvider()
    {
        Team team = gamesProvider.getGamesQuery().getTeam();
        return this.getPlayersProvider(team);
    } 
    
    
    /**
     * There can be a Players provider for any opposing team as well.
     */
    @Override
    public PlayersProvider getPlayersProvider(final Team team)
    {
        if (AbstractClientFactory.playersProviders.get(team) == null)
        {
            AbstractClientFactory.playersProviders.put(team, 
                    new PlayersProvider(eventBus, gamesProvider, team));
        }
        return AbstractClientFactory.playersProviders.get(team);
    }  
    
    @Override
    public AppContainer getAppContainer()
    {        
        return appContainer;
    }   
    
    @Override
    public ShowGamesView getShowGamesView()
    {       
        return showGamesView;
    }    
    
    @Override
    public SelectStatsView getSelectStatsView()
    {
        return selectStatsView;
    }
    
    @Override
    public ShowStatsView getShowStatsView()
    {
        return showStatsView;
    }


    @Override
    public SelectGamesView getSelectGamesView()
    {
        return selectGamesView;
    }    
}
